【ChatGPT】GPT-4でPythonの画像ビューワを作成してみた
新規事業統括部の山本です。
今日OpenAIのChatGPTのモデルとして、GPT-4が利用可能になりました。早速使ってみようと思います。
やってみる
今回は画像のビューワを作成してみます。ちょうどデータセットの画像や、画像モデルに入力した結果を表示するツールがほしいと思っていました。
import os import tkinter as tk from tkinter import filedialog from PIL import Image, ImageTk def browse_folder(): folder_path = filedialog.askdirectory() if not folder_path: return images_frame.delete("all") load_images(folder_path) def load_images(folder_path): supported_extensions = [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff"] for file in os.listdir(folder_path): if os.path.splitext(file)[-1].lower() in supported_extensions: file_path = os.path.join(folder_path, file) display_image(file_path) def display_image(file_path): image = Image.open(file_path) image.thumbnail((100, 100)) tk_image = ImageTk.PhotoImage(image) image_label = tk.Label(images_frame, image=tk_image) image_label.image = tk_image image_label.pack(side=tk.LEFT, padx=5, pady=5) root = tk.Tk() root.title("画像ビューワ") browse_button = tk.Button(root, text="フォルダを選択", command=browse_folder) browse_button.pack(padx=10, pady=10) images_frame = tk.Canvas(root) images_frame.pack() root.mainloop()
実際に動かしてみると、画像が一列に表示がされました。
かなり曖昧な表現でも、対応してくれました。また、コード以外にも動作についての説明もしてくれました。(自分ではコード書かなくても、動作するプログラムが生成されたのは素晴らしいですね!)
修正する
上のプログラムの動作は指示したとおりですが、やはりタイル状に表示してほしいところです。以下のようにお願いしてみました。
import os import tkinter as tk from tkinter import filedialog from PIL import Image, ImageTk def browse_folder(): folder_path = filedialog.askdirectory() if not folder_path: return images_frame.delete("all") load_images(folder_path) def load_images(folder_path): global image_paths supported_extensions = [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff"] image_paths = [os.path.join(folder_path, file) for file in os.listdir(folder_path) if os.path.splitext(file)[-1].lower() in supported_extensions] display_images(0) def display_images(start_index): global current_start_index current_start_index = start_index images_frame.delete("all") for i in range(start_index, min(start_index + 100, len(image_paths))): file_path = image_paths[i] image = Image.open(file_path) image.thumbnail((100, 100)) tk_image = ImageTk.PhotoImage(image) image_label = tk.Label(images_frame, image=tk_image) image_label.image = tk_image image_label.grid(row=i // 10, column=i % 10, padx=5, pady=5) def prev_images(): global current_start_index new_start_index = max(current_start_index - 100, 0) display_images(new_start_index) def next_images(): global current_start_index new_start_index = min(current_start_index + 100, len(image_paths) - 1) display_images(new_start_index) root = tk.Tk() root.title("画像ビューワ") current_start_index = 0 image_paths = [] browse_button = tk.Button(root, text="フォルダを選択", command=browse_folder) browse_button.pack(padx=10, pady=10) prev_button = tk.Button(root, text="前へ", command=prev_images) prev_button.pack(side=tk.LEFT, padx=10, pady=10) next_button = tk.Button(root, text="次へ", command=next_images) next_button.pack(side=tk.RIGHT, padx=10, pady=10) images_frame = tk.Canvas(root) images_frame.pack() root.mainloop()
このコードを動かすと、以下のようになりました。
ただ「次へ」のボタンを押すと、下に追加されてしまいました。
さらに修正する
次へを押したときは、次のページの画像のみ表示されてほしいので、以下のようにお願いしてみました。
import os import tkinter as tk from tkinter import filedialog from PIL import Image, ImageTk def browse_folder(): folder_path = filedialog.askdirectory() if not folder_path: return clear_images() load_images(folder_path) def load_images(folder_path): global image_paths supported_extensions = [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff"] image_paths = [os.path.join(folder_path, file) for file in os.listdir(folder_path) if os.path.splitext(file)[-1].lower() in supported_extensions] display_images(0) def display_images(start_index): global current_start_index current_start_index = start_index clear_images() for i in range(start_index, min(start_index + 100, len(image_paths))): file_path = image_paths[i] image = Image.open(file_path) image.thumbnail((100, 100)) tk_image = ImageTk.PhotoImage(image) image_label = tk.Label(images_frame, image=tk_image) image_label.image = tk_image image_label.grid(row=i // 10, column=i % 10, padx=5, pady=5) def clear_images(): for widget in images_frame.winfo_children(): widget.grid_forget() def prev_images(): global current_start_index new_start_index = max(current_start_index - 100, 0) display_images(new_start_index) def next_images(): global current_start_index new_start_index = min(current_start_index + 100, len(image_paths) - 1) display_images(new_start_index) root = tk.Tk() root.title("画像ビューワ") current_start_index = 0 image_paths = [] browse_button = tk.Button(root, text="フォルダを選択", command=browse_folder) browse_button.pack(padx=10, pady=10) prev_button = tk.Button(root, text="前へ", command=prev_images) prev_button.pack(side=tk.LEFT, padx=10, pady=10) next_button = tk.Button(root, text="次へ", command=next_images) next_button.pack(side=tk.RIGHT, padx=10, pady=10) images_frame = tk.Frame(root) images_frame.pack() root.mainloop()
実行すると、以下のようになりました。(良い感じですね!)
機能を追加してみる
画像を見るだけなら上記で十分ですが、ツールとしては何かの結果を表示しながら比較したところです。そのためは別ウィンドウでボタンやスライダーを作成し、それに応じて表示を切り替えるようにしたいです。
今回は、UIやコールバック部分のコードを生成してもらうために、ボタンを作成しモノクロにするようにお願いしてみました。
import os import tkinter as tk from tkinter import filedialog from PIL import Image, ImageTk def browse_folder(): folder_path = filedialog.askdirectory() if not folder_path: return clear_images() load_images(folder_path) def load_images(folder_path): global image_paths supported_extensions = [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff"] image_paths = [os.path.join(folder_path, file) for file in os.listdir(folder_path) if os.path.splitext(file)[-1].lower() in supported_extensions] display_images(0) def display_images(start_index): global current_start_index current_start_index = start_index clear_images() for i in range(start_index, min(start_index + 100, len(image_paths))): file_path = image_paths[i] image = Image.open(file_path) if grayscale_var.get(): image = image.convert("L") image.thumbnail((100, 100)) tk_image = ImageTk.PhotoImage(image) image_label = tk.Label(images_frame, image=tk_image) image_label.image = tk_image image_label.grid(row=i // 10, column=i % 10, padx=5, pady=5) def clear_images(): for widget in images_frame.winfo_children(): widget.grid_forget() def prev_images(): global current_start_index new_start_index = max(current_start_index - 100, 0) display_images(new_start_index) def next_images(): global current_start_index new_start_index = min(current_start_index + 100, len(image_paths) - 1) display_images(new_start_index) def toggle_grayscale(): display_images(current_start_index) root = tk.Tk() root.title("画像ビューワ") current_start_index = 0 image_paths = [] browse_button = tk.Button(root, text="フォルダを選択", command=browse_folder) browse_button.pack(padx=10, pady=10) prev_button = tk.Button(root, text="前へ", command=prev_images) prev_button.pack(side=tk.LEFT, padx=10, pady=10) next_button = tk.Button(root, text="次へ", command=next_images) next_button.pack(side=tk.RIGHT, padx=10, pady=10) images_frame = tk.Frame(root) images_frame.pack() settings_window = tk.Toplevel(root) settings_window.title("設定") grayscale_var = tk.BooleanVar() grayscale_toggle = tk.Checkbutton(settings_window, text="モノクロ表示", variable=grayscale_var, command=toggle_grayscale) grayscale_toggle.pack(padx=10, pady=10) root.mainloop()
実行してみると、以下のようになりました。トグルボタンではありませんが、チェックボックスをクリックすると、モノクロ・カラーが切り替わって表示されるようになりました。
ベースのコードとしては、かなり良いものができたように思えます。これを真似して、色々なビューワツールが作成できそうです。また、さらに欲しい機能ができたら、再度ChatGPTにお願いすることもできそうです。
型定義を加えてもらう
型アノテーションの無いPythonは扱いにくいので、型定義を加えておいてもらいましょう。
import os from typing import List import tkinter as tk from tkinter import filedialog from PIL import Image, ImageTk def browse_folder() -> None: folder_path = filedialog.askdirectory() if not folder_path: return clear_images() load_images(folder_path) def load_images(folder_path: str) -> None: global image_paths supported_extensions = [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff"] image_paths = [os.path.join(folder_path, file) for file in os.listdir(folder_path) if os.path.splitext(file)[-1].lower() in supported_extensions] display_images(0) def display_images(start_index: int) -> None: global current_start_index current_start_index = start_index clear_images() for i in range(start_index, min(start_index + 100, len(image_paths))): file_path = image_paths[i] image = Image.open(file_path) if grayscale_var.get(): image = image.convert("L") image.thumbnail((100, 100)) tk_image = ImageTk.PhotoImage(image) image_label = tk.Label(images_frame, image=tk_image) image_label.image = tk_image image_label.grid(row=i // 10, column=i % 10, padx=5, pady=5) def clear_images() -> None: for widget in images_frame.winfo_children(): widget.grid_forget() def prev_images() -> None: global current_start_index new_start_index = max(current_start_index - 100, 0) display_images(new_start_index) def next_images() -> None: global current_start_index new_start_index = min(current_start_index + 100, len(image_paths) - 1) display_images(new_start_index) def toggle_grayscale() -> None: display_images(current_start_index) root = tk.Tk() root.title("画像ビューワ") current_start_index: int = 0 image_paths: List[str] = [] browse_button = tk.Button(root, text="フォルダを選択", command=browse_folder) browse_button.pack(padx=10, pady=10) prev_button = tk.Button(root, text="前へ", command=prev_images) prev_button.pack(side=tk.LEFT, padx=10, pady=10) next_button = tk.Button(root, text="次へ", command=next_images) next_button.pack(side=tk.RIGHT, padx=10, pady=10) images_frame = tk.Frame(root) images_frame.pack() settings_window = tk.Toplevel(root) settings_window.title("設定") grayscale_var = tk.BooleanVar() grayscale_toggle = tk.Checkbutton(settings_window, text="モノクロ表示", variable=grayscale_var, command=toggle_grayscale) grayscale_toggle.pack(padx=10, pady=10) root.mainloop()
(補足:まとめて実行すると)
新しくChatを作成し、今までの指示をまとめて渡してみました。
import os import tkinter as tk from itertools import islice from tkinter import filedialog from PIL import Image, ImageOps, ImageTk class ImageGallery: def __init__(self, master): self.master = master self.master.title("画像ビューワ") self.image_canvas = tk.Canvas(self.master, width=800, height=800) self.image_canvas.pack() self.toggle_button = tk.Button(self.master, text="モノクロ切り替え", command=self.toggle_monochrome) self.toggle_button.pack() self.next_button = tk.Button(self.master, text="次へ", command=self.next_page) self.next_button.pack(side=tk.RIGHT) self.prev_button = tk.Button(self.master, text="前へ", command=self.prev_page) self.prev_button.pack(side=tk.RIGHT) self.monochrome = False self.current_page = 0 self.images_per_page = 100 self.images = [] self.folder_path = "" def load_images(self): self.folder_path = filedialog.askdirectory() if not self.folder_path: return self.images = [] for filename in os.listdir(self.folder_path): if filename.lower().endswith((".png", ".jpg", ".jpeg", ".gif", ".bmp")): self.images.append(os.path.join(self.folder_path, filename)) self.current_page = 0 self.display_images() def display_images(self): self.image_canvas.delete("all") x = 0 y = 0 max_width = self.image_canvas.winfo_width() max_height = self.image_canvas.winfo_height() for image_path in islice( self.images, self.current_page * self.images_per_page, (self.current_page + 1) * self.images_per_page ): img = Image.open(image_path) img.thumbnail((max_width // 10, max_height // 10)) if self.monochrome: img = ImageOps.grayscale(img) tk_img = ImageTk.PhotoImage(img) self.image_canvas.create_image(x, y, anchor=tk.NW, image=tk_img) self.image_canvas.image = tk_img x += img.width if x >= max_width: x = 0 y += img.height def next_page(self): if (self.current_page + 1) * self.images_per_page < len(self.images): self.current_page += 1 self.display_images() def prev_page(self): if self.current_page > 0: self.current_page -= 1 self.display_images() def toggle_monochrome(self): self.monochrome = not self.monochrome self.display_images() def main(): root = tk.Tk() app = ImageGallery(root) app.load_images() root.mainloop() if __name__ == "__main__": main()
実行してみると、以下のようなウィンドウが表示されました。(意図した動作とは、異なる感じですね)
まとめ・感想
GPT-4を利用したChatGPTで、Pythonプログラムの画像ビューワを作成しました。自分でコードを書かずに、欲しいプログラムのベース部分を作成することができ、大幅に作業効率を上げることができそうです。また、プログラムを実行しながら、自分がほしいと思っている機能を考えながら順次付け加えていくことができ、非常に便利だと思いました。